/**
 * This class gives the police officers to perform the different
 * operations to the server
 */
package com.comp6231.replica2;

import java.util.Hashtable;
import java.util.Random;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.comp6231.replica2.server.helper.ClientCORBAObject;
import com.comp6231.replica2.server.helper.CountObject;
import com.comp6231.replica2.server.helper.IndividualsStatus;
import com.comp6231.replica2.server.helper.Log;
import com.comp6231.replica2.server.helper.StationID;
import com.comp6231.replica2.server.helper.StationInterfaceIDL;
import com.comp6231.replica2.server.helper.Utility;

/**
 * @author Farzana Alam
 * 
 */
public class CorbaClient {

	private StationID stationID;
	private StationInterfaceIDL stationServer;
	private String badgeID;

	// just to support the test case. will be removed before deployment
	public void setBadgeID(String badgeID) {
		this.badgeID = badgeID;
	}

	/*
	 * This is a place holder for lastName-recordID which will be used in test
	 * cases while testing editCRecord method
	 */
	public static Hashtable<String, String> recordLog = new Hashtable<>();

	/*
	 * This lock is used to avoid concurrent modification of recordLog
	 */
	private static final Object lock = new Object();

	/*
	 * These two fields are used to get lastName and recordID from Hashtable
	 * recordLog
	 */
	private static Object[] recordArray;
	private static int index;

	/**
	 * @param badgeID
	 * @param name
	 */
	private CorbaClient(String badgeID) {
		this.badgeID = badgeID;
		setStationID(this.badgeID);
		this.stationServer = ClientCORBAObject.getCORBAObj(stationID);
	}

	/**
	 * @param badgeID
	 * @param name
	 * @return OfficerClient
	 */
	public static CorbaClient getCORBAClient(String badgeID) {
		/*
		 * This method checks if the badgeID matches the exact required pattern
		 * and then returns an OfficerClient object
		 */
		badgeID = badgeID.toUpperCase();

		Pattern p = Pattern.compile("SPVM\\d{4}|SP[BL]\\d{4}");
		Matcher m = p.matcher(badgeID);

		if (m.matches())
			return new CorbaClient(badgeID);
		else {
			System.out.println("Invalid BadgeID. Please try again! \n");
			return null;
		}

	}

	/**
	 * @param badgeID
	 */
	private void setStationID(String badgeID) {
		/*
		 * This method sets stationID from badgeID
		 */
		if (badgeID.startsWith("SPVM"))
			this.stationID = StationID.SPVM;
		else if (badgeID.startsWith("SPL"))
			this.stationID = StationID.SPL;
		else if (badgeID.startsWith("SPB"))
			this.stationID = StationID.SPB;
		else
			System.out.println("Invalid BadgeID. Please try again! \n");
	}

	protected boolean writeLog(Log log) {
		/*
		 * This method will write Log object to external file
		 */
		boolean writeSuccess = Utility.writeLog(log, this.badgeID + ".log");
		return writeSuccess;
	}

	public String createCRecord(String firstName, String lastName,
			String description, IndividualsStatus status) {
		/*
		 * This method avails the police officer to invoke the remote server
		 * createCRecord method.
		 */
		Log log = new Log();
		log = stationServer.createCRecord(badgeID, firstName, lastName,
				description, status);
		if (log != null) {
			writeLog(log);
			synchronized (lock) {
				recordLog.put(lastName, log.recordID);
			}
			return "Success";
		} else
			return "Fail";
	}

	public String editCRecord(String lastName, String recordID,
			IndividualsStatus newStatus) {
		/*
		 * This method avails the police officer to invoke the remote server
		 * editCRecord method.
		 */
		Log log = new Log();
		log = stationServer.editRecord(badgeID, lastName, recordID, newStatus);
		if (log != null) {
			writeLog(log);
			if (log.operationStatus.equalsIgnoreCase("Fail")) {
				System.out.println("Invalid Record ID or Last Name");
				return "Fail";
			} else
				return "Success";
		} else
			return "Fail";
	}

	public String createMRecord(String firstName, String lastName,
			String address, String lastDate, String lastLocation,
			IndividualsStatus status) {
		/*
		 * This method avails the police officer to invoke the remote server
		 * createMRecord method.
		 */
		Log log = new Log();
		log = stationServer.createMRecord(badgeID, firstName, lastName,
				address, lastDate, lastLocation, status);
		if (log != null) {
			writeLog(log);
			synchronized (lock) {
				recordLog.put(lastName, log.recordID);
			}
			return "Success";
		} else
			return "Fail";
	}

	public String getRecordCounts() {
		/*
		 * This method avails the police officer to invoke the remote server
		 * getRecordCounts method.
		 */
		Log log = new Log();
		CountObject countObj = new CountObject();
		countObj.countResult = "Connection Error";
		countObj = stationServer.getRecordCounts(badgeID);

		// eg. "SPVM: 0, SPB:1, SPL:2"
		// System.out.println(countObj.countResult);
		log = (Log) countObj.log;
		if (log != null) {
			writeLog(log);
			return countObj.countResult;
		} else
			return "Fail";
	}

	public String transferRecord(String recordID, String remoteStationServerName) {
		/*
		 * This method avails the police officer to invoke the remote server
		 * transferRecord method.
		 */
		Log log = new Log();
		log = stationServer.transferReecord(badgeID, recordID,
				remoteStationServerName);
		if (log != null) {
			writeLog(log);
			String[] lastNameArray = log.operationDescription.split(" ");
			String lastName = lastNameArray[lastNameArray.length - 1];
			synchronized (lock) {
				recordLog.remove(lastName);
			}
			return "Success";
		}
		return "Fail";
	}

	public static String[] getRecordEntry() {
		/*
		 * This method returns String array of lastName and recordID pair
		 */
		synchronized (lock) {
			Set<String> recordSet = recordLog.keySet();
			if (recordSet == null || recordSet.isEmpty()) {
				return null;
			}

			recordArray = recordSet.toArray();
			index = new Random().nextInt(recordArray.length);
			String lastName = (String) recordArray[index];
			String recordID = CorbaClient.recordLog.get(lastName);
			System.out.println("\n\n Sending request to edit: " + recordID
					+ " " + lastName);
			return new String[] { lastName, recordID };

		}
	}

	/*
	 * The following codes are for simple code testing. Will be removed before
	 * deployment
	 */

	// public static void main(String[] args) {
	// Scanner input = new Scanner(System.in);
	// boolean flag = true;
	// OfficerClient client;
	// while (flag) {
	// client = OfficerClient.getAnOfficerClient(input.next(),
	// input.next());
	// if (client != null)
	// flag = false;
	// }
	// input.close();
	//
	// OfficerClient officer1 = new OfficerClient("SPVM1234", "Sajir");
	// boolean b = false;
	// for (int i = 1; i < 50; i++) {
	// b = officer1.createCRecord("Foysal", "Iqbal" + i, "Con",
	// IndividualsStatus.ON_THE_RUN);
	// }
	//
	// for (int i = 1; i < 5; i++) {
	// b = officer1.editCRecord("Iqbal" + i, "CR0000" + i,
	// IndividualsStatus.CAPTURED);
	// System.out.println("" + b);
	// }
	// System.out.println("" + b);
	// b = officer1.createMRecord("Farzana", "Alam", "Montreal",
	// "15 / 10 / 20012", "Ottawa", IndividualsStatus.MISSING);
	// System.out.println("" + b);
	// b = officer1.getRecordCounts();
	// System.out.println("" + b);
	//
	// }

}
